package com.etone.smartaudit.wopi.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSON;
import com.etone.smartaudit.wopi.domian.*;
import com.etone.smartaudit.wopi.utils.Base64Util;
import lombok.extern.log4j.Log4j2;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author: sovy
 * @date: 2021/1/28
 * @description:
 */
@Service
@Log4j2
public class WopiService {
    @Autowired
    private LockService lockService;

    @Value("${audit.filePath}")
    private String filePath;

    private static final String CHARSET_UTF8 = "UTF-8";

    @Autowired
    private RedisUtil redisUtil;

    private final static String EDIT_OFFICE = "office_edit_f:";

    @Autowired
    private LockHandService lockHandService;

    /**
     * Retrieves a file from a host.
     *
     * @param name     a file ID of a file managed by host
     * @param response
     */
    public void getFile(String name, HttpServletResponse response) {
        name = Base64Util.decode(name);
        String path = filePath + name;
        File file = new File(path);
        String filename = file.getName();
        try (InputStream fis = new BufferedInputStream(new FileInputStream(path));
             OutputStream toClient = new BufferedOutputStream(response.getOutputStream())) {
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            response.reset();
            response.addHeader("Content-Disposition", "attachment;filename=" +
                    new String(filename.getBytes(CHARSET_UTF8), "ISO-8859-1"));
            response.addHeader("Content-Length", String.valueOf(file.length()));

            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            toClient.write(buffer);
            toClient.flush();
        } catch (IOException e) {
            response.setStatus(WopiStatus.NOT_FOUND.value());
            log.error("getFile failed, errMsg: {}", e);
        }
    }

    /**
     * Updates a file’s binary contents.
     *
     * @param name    a file ID of a file managed by host
     * @param content the full binary contents of the file
     * @param request
     * @return
     */
    public ResponseEntity postFile(String name, byte[] content, HttpServletRequest request) {
        ResponseEntity response;
        Map<String, String[]> map = request.getParameterMap();
        log.info(map);
        name = Base64Util.decode(name);
        String requestLock = request.getHeader(WopiRequestHeader.LOCK);
        File file = new File(filePath + name);
        if (file.exists()) {
            response = lockService.putFile(request, name, file, content);
            //此处监听文件编辑
            StringBuilder sb = new StringBuilder();
            //获取文件id 获取用户id
            String ownerId = request.getParameter("ownerId");
            String fileId = request.getParameter("fileId");
            if (!StringUtils.isEmpty(fileId)) {
                sb.append("files").append("_").append(fileId);
                String key = String.format("%s%s", EDIT_OFFICE, DigestUtil.md5Hex(sb.toString()));
                if (redisUtil.hasKey(key)) {
                    redisUtil.delete(key);
                }
                redisUtil.setEx(key, sb.toString(), 3, TimeUnit.MINUTES);
            }
            if (response.getStatusCodeValue() != WopiStatus.OK.value()) {
                log.warn("update {} failed, status: {}", name, response);
            }
        } else {
            response = ResponseEntity.status(WopiStatus.NOT_FOUND.value()).build();
            log.error("postFile failed, file not found");
        }
        log.info("postFile -- filename: {}, response: {} , requestLock: {}", name, response, requestLock);
        return response;
    }

    /**
     * Returns information about a file, a user’s permissions on that file,
     * and general information about the capabilities that the WOPI host has on the file.
     *
     * @param fileName a file ID of a file managed by host
     * @param userName
     * @param request
     * @return
     * @throws Exception
     */
    public ResponseEntity<FileInfo> getFileInfo(String fileName, String userName, HttpServletRequest request) throws Exception {
        fileName = Base64Util.decode(fileName);
        String userInfo = Base64Util.decode(userName);
        FileInfo info = new FileInfo();
        if (fileName != null && fileName.length() > 0) {
            File file = new File(filePath + fileName);
            if (file.exists()) {
                info.setBaseFileName(file.getName());
                info.setSize(file.length());
                info.setOwnerId("admin");//request.getParameter("ownerId")
                info.setVersion(file.lastModified());
                info.setSha256(getHash256(file));
//                info.setUserFriendlyName(StringUtils.isEmpty(userName) ? "Guest" : URLDecoder.decode(userInfo, "utf-8"));
                info.setUserFriendlyName("");
                info.setUserCanWrite("edit".equals(request.getParameter("action")));
//                info.setUserInfo("测试账号:" + RandomUtil.randomString(2));
//                info.setHostName("hostName:" + RandomUtil.randomString(2));
//                info.setUserId("userId:" + RandomUtil.randomString(2));
                info.setWebEditingDisabled(false);
            } else {
                return ResponseEntity.status(WopiStatus.NOT_FOUND.value()).build();
            }
        }
        return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(info);
    }

    public ResponseEntity handleLock(String fileName, HttpServletRequest request) {
//        return  lockHandService.handleLock(fileName,request);
        fileName = Base64Util.decode(fileName);
        ResponseEntity response = null;
        //判断当前文件是否已经上锁
        String wopiOverride = request.getHeader(WopiRequestHeader.OVERRIDE);
        String requestLock = request.getHeader(WopiRequestHeader.LOCK);
        String oldLock = request.getHeader(WopiRequestHeader.OLD_LOCK);
        switch (wopiOverride) {
            case "LOCK":
                if (oldLock != null) {
                    wopiOverride = "UNLOCK_AND_RELOCK";
                    response = lockService.unlockAndRelock(request, fileName);
                } else {
                    response = lockService.lock(request, fileName);
                }
                break;
            case "GET_LOCK":
                response = lockService.getLock(request, fileName);
                break;
            case "REFRESH_LOCK":
                response = lockService.refreshLock(request, fileName);
                break;
            case "UNLOCK":
                response = lockService.unlock(request, fileName);
                break;
            default:
                response = ResponseEntity.status(WopiStatus.NOT_IMPLEMENTED.value()).build();
                break;
        }
        log.info("handleLock -- filename: {}, override: {}, response: {}, requestLock: {}, oldLock: {}",
                fileName, wopiOverride, response, requestLock, oldLock);
        return response;
//        return ResponseEntity.ok().build();
    }

    /**
     * Get SHA-256 value of file
     *
     * @param file
     * @return
     */
    private String getHash256(File file) throws IOException, NoSuchAlgorithmException {
        String value = "";
        try (InputStream fis = new FileInputStream(file)) {
            byte[] buffer = new byte[1024];
            int numRead;
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            do {
                numRead = fis.read(buffer);
                if (numRead > 0) {
                    digest.update(buffer, 0, numRead);
                }
            } while (numRead != -1);
            value = new String(Base64.encodeBase64(digest.digest()));
        }
        return value;
    }

    /***
     * 根据路径获取文件
     * @param fileName
     * @param userName
     * @param path
     * @return
     */
    public ResponseEntity<FileInfo> getPathFileInfo(String fileName, String userName, String path) throws IOException, NoSuchAlgorithmException {
        fileName = Base64Util.decode(fileName);
        FileInfo info = new FileInfo();
        if (fileName != null && fileName.length() > 0) {
            File file = new File(filePath + Base64Util.decode(path));
            if (file.exists()) {
                info.setBaseFileName(file.getName());
                info.setSize(file.length());
                info.setOwnerId("admin");
                info.setVersion(file.lastModified());
                info.setSha256(getHash256(file));
                info.setUserFriendlyName(StringUtils.isEmpty(userName) ? "Guest" : "122");
            } else {
                return ResponseEntity.status(WopiStatus.NOT_FOUND.value()).build();
            }
        }
        return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(info);
    }
}
